/**
 * @file shader.h
 * @author Young_jam
 * @brief 负责加载和编译着色器
 * @version 1.0
 * @date 2023/01/13
 */
#pragma once

#include <GLES3/gl3.h>
#include <map>
#include <string>

#include "c_defines.h"

class CShader {
public:
    CShader() = default;
    ~CShader();

    /**
     * @brief 加载着色器程序
     * @param vertexProg 顶点着色器源文件
     * @param fragmentProg 片元着色器源文件
     * @param ... [2, 0, "vVertexPos", 1, "vNormal"]
     * @note 2表示顶点程序含有两个属性,索引0对应vVertexPos,索引1对应属性vNormal
     */
    bool LoadShader(const char *vertexProg, const char *fragmentProg, ...);

    void Enable() { glUseProgram(m_Id); }

    void Disable() { glUseProgram(0); }

    void DeleteShader()
    {
        if (m_Id != 0)
        {
            glDeleteProgram(m_Id);
            m_Id = 0;
        }
    }

    bool SetUniformMatrix4fv(const std::string &uniform, const GLfloat *value, GLsizei count = 1,
                             GLboolean transpose = GL_FALSE)
    {
        auto loc = GetUniformLocation(uniform);
        if (loc >= 0 && value)
        {
            glUniformMatrix4fv(loc, count, transpose, value);
            return true;
        }
        else
            return false;
    }

    bool SetUniform3fv(const std::string &uniform, const GLfloat *value, GLsizei count = 1)
    {
        auto loc = GetUniformLocation(uniform);
        if (loc >= 0 && value)
        {
            glUniform3fv(loc, count, value);
            return true;
        }
        else
            return false;
    }

    // todo 根据实际需求增加uniform设置

private:
    bool LoadShaderSourceCode(const char *file, GLuint shader);

    GLint GetUniformLocation(const std::string &uniform)
    {
        GLint loc = -1;

        auto iter = m_mapUniforms.find(uniform);
        if (iter == m_mapUniforms.end())
        {
            loc = glGetUniformLocation(m_Id, uniform.c_str());
            if (loc < 0)
                WARN("get %s loc failed!", uniform.c_str());
            else
                m_mapUniforms.emplace(uniform, loc);
        }
        else
            loc = iter->second;

        return loc;
    }

private:
    static const GLuint MaxShaderLength = 8192;

    GLchar m_codeString[MaxShaderLength]{};

private:
    GLuint                        m_Id{0};
    std::map<std::string, GLuint> m_mapUniforms;
};
